; DRAW using bresenhams algorithm and screen positioning
; Copyleft (k) 2010 by J. Rodriguez (a.k.a. Boriel) http://www.boriel.com
; vim:ts=4:et:sw=4:
; Y parameter in A
; X parameter in high byte on top of the stack
;
#include once <error.asm>
#include once <in_screen.asm>
#include once <cls.asm>
#include once <SP/PixelDown.asm>
#include once <SP/PixelUp.asm>
#include once <SP/PixelLeft.asm>
#include once <SP/PixelRight.asm>
;
;; DRAW PROCEDURE
                     ;- PROC
                     ;- LOCAL __DRAW1
                     ;- LOCAL __DRAW2
                     ;- LOCAL __DRAW3
                     ;- LOCAL __DRAW4, __DRAW4_LOOP
                     ;- LOCAL __DRAW5
                     ;- LOCAL __DRAW6, __DRAW6_LOOP
                     ;- LOCAL __DRAW_ERROR
                     ;- LOCAL DX1, DX2, DY1, DY2
                     ;- LOCAL __INCX, __INCY, __DECX, __DECY
                     ;- LOCAL P_FLAG
                     ;- P_FLAG EQU 23697

__DRAW_ERROR:
                     ;- jp __OUT_OF_SCREEN_ERR

DRAW:
;; ENTRY POINT
                     ;- LOCAL PIXEL_ADDR
                     ;- LOCAL COORDS
                     ;- LOCAL __DRAW_SETUP1, __DRAW_START, __PLOTOVER, __PLOTINVERSE
lda z80_e            ;- ex de,hl ; DE = Y OFFSET
ldx z80_l
stx z80_e
sta z80_l
lda z80_d
ldx z80_h
stx z80_d
sta z80_h
                     ;- pop hl	; return addr
                     ;- ex (sp), hl ; CALLEE => HL = X OFFSET
                     ;- ld bc, (COORDS)
                     ;- ld a, c
                     ;- add a, l
                     ;- ld l, a
                     ;- ld a, h
                     ;- adc a, 0 ; HL = HL + C
                     ;- ld h, a
                     ;- jr nz, __DRAW_ERROR	; if a <> 0 => Out of Screen
                     ;- ld a, b
                     ;- add a, e
                     ;- ld e, a
                     ;- ld a, d
                     ;- adc a, 0 ; DE = DE + B
                     ;- ld d, a
                     ;- jr nz, __DRAW_ERROR	; if a <> 0 => Out of Screen
                     ;- ld a, 191
                     ;- sub e
                     ;- jr c, __DRAW_ERROR	; Out of screen
                     ;- ld h, e			; now H,L = y2, x2

__DRAW:
; __FASTCALL__ Entry. Plots from (COORDS) to coord H, L
                     ;- push hl
                     ;- ex de, hl		; D,E = y2, x2;
                     ;- ld a, (P_FLAG)
                     ;- ld c, a
                     ;- bit 2, a        ; Test for INVERSE1
                     ;- jr z, __DRAW_SETUP1
                     ;- ld a, 2Fh       ; CPL
                     ;- ld (__PLOTINVERSE), a
                     ;- ld a, 0A6h      ; and (hl)
                     ;- jp __DRAW_START

__DRAW_SETUP1:
                     ;- xor a           ; nop
                     ;- ld (__PLOTINVERSE), a
                     ;- ld a, 0B6h      ; or (hl)
                     ;- bit 0, c        ; Test for OVER
                     ;- jr z, __DRAW_START
                     ;- ld a, 0AEh      ; xor (hl)

__DRAW_START:
                     ;- ld (__PLOTOVER), a ; "Pokes" last operation
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- ld bc, (COORDS) ; B'C' = y1, x1
                     ;- ld d, b         ; Saves B' in D'
                     ;- ld a, 191
                     ;- LOCAL __PIXEL_ADDR
__PIXEL_ADDR EQU 22ACh
                     ;- call __PIXEL_ADDR
;; Now gets pixel mask in A register
                     ;- ld b, a
                     ;- inc b
                     ;- xor a
                     ;- scf
                     ;- LOCAL __PIXEL_MASK
__PIXEL_MASK:
                     ;- rra
                     ;- djnz __PIXEL_MASK
                     ;- ld b, d         ; Restores B' from D'
                     ;- pop de			; D'E' = y2, x2
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
; At this point: D'E' = y2,x2 coords
; B'C' = y1, y1  coords
                     ;- ex af, af'      ; Saves A reg for later
; A' = Pixel mask
; H'L' = Screen Address of pixel
                     ;- ld bc, (COORDS) ; B,C = y1, x1
                     ;- ld a, e
                     ;- sub c			; dx = X2 - X1
                     ;- ld c, a			; Saves dx in c
                     ;- ld a, 0Ch       ; INC C opcode
                     ;- ld hl, __INCX   ; xi = 1
                     ;- jr nc, __DRAW1
                     ;- ld a, c
                     ;- neg		 		; dx = X1 - X2
                     ;- ld c, a
                     ;- ld a, 0Dh       ; DEC C opcode
                     ;- ld hl, __DECX   ; xi = -1

__DRAW1:
                     ;- ld (DX1), a
                     ;- ld (DX1 + 2), hl ; Updates DX1 call address
                     ;- ld (DX2), a
                     ;- ld (DX2 + 2), hl ; Updates DX2 call address
                     ;- ld a, d
                     ;- sub b			; dy = Y2 - Y1
                     ;- ld b, a			; Saves dy in b
                     ;- ld a, 4         ; INC B opcode
                     ;- ld hl, __INCY   ; y1 = 1
                     ;- jr nc, __DRAW2
                     ;- ld a, b
                     ;- neg
                     ;- ld b, a         ; dy = Y2 - Y1
                     ;- ld a, 5         ; DEC B opcode
                     ;- ld hl, __DECY   ; y1 = -1

__DRAW2:
                     ;- ld (DY1), a
                     ;- ld (DY1 + 2), hl ; Updates DX1 call address
                     ;- ld (DY2), a
                     ;- ld (DY2 + 2), hl ; Updates DX2 call address
                     ;- ld a, b
                     ;- sub c			; dy - dx
                     ;- jr c, __DRAW_DX_GT_DY	; DX > DY
; At this point DY >= DX
; --------------------------
; HL = error = dY / 2
                     ;- ld h, 0
                     ;- ld l, b
                     ;- srl l
; DE = -dX
                     ;- xor a
                     ;- sub c
                     ;- ld e, a
                     ;- sbc a, a
                     ;- ld d, a
; BC = DY
                     ;- ld c, b
                     ;- ld b, h
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- scf             ; Sets Carry to signal update ATTR
                     ;- ex af, af'      ; Brings back pixel mask
                     ;- ld e, a         ; Saves it in free E register
                     ;- jp __DRAW4_LOOP

__DRAW3:			; While c != e => while y != y2
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- add hl, de		; error -= dX
                     ;- bit 7, h		;
lda z80_c            ;- exx				; recover coordinates
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- jr z, __DRAW4	; if error < 0
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- add hl, bc		; error += dY
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- ld a, e
DX1:         ; x += xi
inc z80_c            ;- inc c
jsr __INCX           ;- call __INCX     ; This address will be dynamically updated
                     ;- ld e, a

__DRAW4:
DY1:
; y += yi
inc z80_b            ;- inc b
jsr __INCY           ;- call __INCY     ; This address will be dyncamically updated
                     ;- ld a, e         ; Restores A reg.
jsr __FASTPLOT       ;- call __FASTPLOT

__DRAW4_LOOP:
                     ;- ld a, b
                     ;- cp d
                     ;- jp nz, __DRAW3
                     ;- ld (COORDS), bc
rts                  ;- ret

__DRAW_DX_GT_DY:	; DX > DY
; --------------------------
; HL = error = dX / 2
                     ;- ld h, 0
                     ;- ld l, c
                     ;- srl l			; HL = error = DX / 2
; DE = -dY
                     ;- xor a
                     ;- sub b
                     ;- ld e, a
                     ;- sbc a, a
                     ;- ld d, a
; BC = dX
                     ;- ld b, h
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- ld d, e
                     ;- scf             ; Sets Carry to signal update ATTR
                     ;- ex af, af'      ; Brings back pixel mask
                     ;- ld e, a         ; Saves it in free E register
                     ;- jp __DRAW6_LOOP

__DRAW5:			; While loop
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- add hl, de		; error -= dY
                     ;- bit 7, h		; if (error < 0)
lda z80_c            ;- exx				; Restore coords
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- jr z, __DRAW6	;
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
                     ;- add hl, bc		; error += dX
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp

DY2:                 ; y += yi
                     ;- inc b
jsr __INCY           ;- call __INCY     ; This address will be dynamically updated

__DRAW6:
lda z80_e            ;- ld a,e
sta z80_a

DX2:          ; x += xi
                     ;- inc c
jsr __INCX           ;- call __INCX     ; This address will be dynamically updated
lda z80_a            ;- ld e,a
sta z80_e
jsr __FASTPLOT       ;- call __FASTPLOT

__DRAW6_LOOP:
lda z80_c            ;- ld a,c			; Current X coord
sta z80_a
                     ;- cp d
                     ;- jp nz, __DRAW5
                     ;- ld (COORDS), bc
rts                  ;- ret

PIXEL_ADDR	EQU 22ACh
COORDS   EQU 5C7Dh

__DRAW_END:
lda z80_c            ;- exx
ldx z80_cp
stx z80_c
sta z80_cp
lda z80_b
ldx z80_bp
stx z80_b
sta z80_bp
lda z80_e
ldx z80_ep
stx z80_e
sta z80_ep
lda z80_d
ldx z80_dp
stx z80_d
sta z80_dp
lda z80_l
ldx z80_lp
stx z80_l
sta z80_lp
lda z80_h
ldx z80_hp
stx z80_h
sta z80_hp
rts                  ;- ret
;
;; Given a A mask and an HL screen position
;; return the next left position
;; Also updates BC coords
__DECX EQU SP.PixelLeft
;; Like the above, but to the RIGHT
;; Also updates BC coords
__INCX EQU SP.PixelRight
;; Given an HL screen position, calculates
;; the above position
;; Also updates BC coords
__INCY EQU SP.PixelUp
;; Given an HL screen position, calculates
;; the above position
;; Also updates BC coords
__DECY EQU SP.PixelDown
;; Puts the A register MASK in (HL)
__FASTPLOT:
__PLOTINVERSE:
                     ;- nop         ; Replace with CPL if INVERSE 1
__PLOTOVER:
ldy #$00             ;- or (hl)
ora (z80_hl),y
; Replace with XOR (hl) if OVER 1 AND INVERSE 0
; Replace with AND (hl) if INVERSE 1
lda z80_a            ;- ld (hl),a
ldy #$00
sta (z80_hl),y
ldx z80_ap           ;- ex af, af'  ; Recovers flag. If Carry set => update ATTR
sta z80_ap
txa
lda z80_e            ;- ld a,e     ; Recovers A reg
sta z80_a
bcc *+3              ;- ret nc
rts
lda z80_l            ;- push hl
pha
lda z80_h
pha
lda z80_e            ;- push de
pha
lda z80_d
pha
;; gets ATTR position with offset given in SCREEN_ADDR
lda z80_h            ;- ld a,h
sta z80_a
                     ;- rrca
                     ;- rrca
                     ;- rrca
and #$03             ;- and $03
ora #$18             ;- or $18
lda z80_a            ;- ld h,a
sta z80_h
lda SCREEN_ADDR      ;- ld de,(SCREEN_ADDR)
sta z80_e
lda SCREEN_ADDR+1
sta z80_d
                     ;- add hl,de  ;; Final screen addr
LOCAL PO_ATTR_2
PO_ATTR_2 EQU 0BE4h  ; Another entry to PO_ATTR
jsr PO_ATTR_2        ;- call PO_ATTR_2   ; This will update attr accordingly. Beware, uses IY
pla                  ;- pop de
sta z80_d
pla
sta z80_e
pla                  ;- pop hl
sta z80_h
pla
sta z80_l
                     ;- LOCAL __FASTPLOTEND
__FASTPLOTEND:
ora z80_a            ;- or a        ; Resets carry flag
ldx z80_ap           ;- ex af, af'  ; Recovers A reg
sta z80_ap
txa
lda z80_e            ;- ld a,e
sta z80_a
rts                  ;- ret
                     ;- ENDP

